package core;

import java.awt.Point;
import java.util.ArrayList;
import java.util.List;

import card.CreatureCard;

/**
 * 
 * @author antongr
 * The Object for handling Battles. 
 * Supports battle between two different players.
 */
public class Battle {
	private Tile[] tile = new Tile[2];
	private Player[] player = new Player[2];
	private boolean[] isReadyToFight = new boolean[2];
	private List<CreatureCard>[] creatures = new ArrayList[2];
	private static Battle me;
	public enum BattleState {TARGETING, HASBATTLED};
	public BattleState battleState = BattleState.TARGETING;
	
	/**
	 * The targets for all creatures. A value of -1 means no target has been set.
	 */
	private int[][] attackOrder = new int[2][Constants.MAXCREATURESONTILE];
	/**
	 * Keeps record of how much damage each creature took this battle. 
	 */
	private int[][] damageTaken = new int[2][Constants.MAXCREATURESONTILE];
		/**
	 * Similar to singleton design, but a new battleObject will be created by initiateBattle(Tile, Tile);
	 * @return
	 */
	public static Battle getInstance(){
		if(me == null)
			me=new Battle();
		return me;
	}
	
	/**
	 * Assumes that both tiles actually contain Creatures from different players.
	 * @param attackingTile
	 * @param defendingTile
	 */
	public void initiateBattle(Tile attackingTile, Tile defendingTile){
		reset();
		tile[0] = attackingTile;
		tile[1] = defendingTile;
		for(int i = 0; i<=1; i++){
			creatures[i] = tile[i].getCreatures(); 
			player[i] = creatures[i].get(0).getOwner();
		}
	}
	
	private void reset(){
		tile[0] = null;
		tile[1] = null;
		for(int i = 0; i<=1; i++){
			creatures[i].clear(); 
			isReadyToFight[i] = false;
			player[i]=null;
			for(int t=0; t<Constants.MAXCREATURESONTILE; t++){
				attackOrder[i][t]=-1;
				damageTaken[i][t]=0;
			}
		}
		battleState = BattleState.TARGETING;
	}
	
	

	private Battle(){
		creatures[0]=new ArrayList<CreatureCard>();
		creatures[1]=new ArrayList<CreatureCard>();
		reset();
	}
	/**
	 * tells a creature to attack a given creature.
	 * @param attacker Card that is given a target.
	 * @param defender Card that is targeted.
	 */
	public void setTarget(CreatureCard attacker, CreatureCard defender){
		Point attackerPos=find(attacker);
		attackOrder[attackerPos.x][attackerPos.y]=find(defender).y;
	}
	/**
	 * returns the place in the creatures list, with x being the player 0 or 1, 
	 * and y being creature number as seen by this Battle object.
	 * @param card
	 * @return
	 */
	private Point find(CreatureCard card){
		int x=0, y=-1;
		for(int tempX=0; tempX<=1; tempX++){
			int tempY=creatures[tempX].indexOf(card);
			if(tempY != -1){
				y=tempY;
				x=tempX;
			}
				
		}
		if(y == -1)
			return null;
		else
			return new Point(x, y);
		
	}
	
	/**
	 * Returns a list of all creatures on one side of the battle.
	 * @param playerId must be 0 or 1.
	 * @return
	 */
	public List<CreatureCard> getCreatures(int playerId){
		return creatures[playerId];
	}
	
	/**
	 * Makes all Creatures hit their targets simultaneously.
	 */
	public void fightBattle(){
		//TODO priority 1 make battles work.
		
		//Currently only removes all targeted CreatureCards.
		for(int i = 0; i<=1; i++){
			for(CreatureCard attacker : creatures[i]){
				if(attackOrder[i][creatures[i].indexOf(attacker)] != -1){
					attacker.useAction();
					CreatureCard defender = creatures[otherPlayerId(i)].get(attackOrder[i][creatures[i].indexOf(attacker)]);
					
					calculateDamage(attacker, defender);
					if(defender.getCurrentHealth() <= 0){
						tile[otherPlayerId(i)].removeCard(defender);
						defender.getOwner().getDiscardPile().add(defender);
					}				
				}				
			}
		}
		battleState = BattleState.HASBATTLED;
	}
	public void calculateDamage(CreatureCard attacker, CreatureCard defender){
		int totalDamage = 0;
		if ( RandomGenerator.getRandomValue(attacker.getMinDmg(), attacker.getMaxDmg())-defender.getActualDefense() > 0)
			totalDamage = RandomGenerator.getRandomValue(attacker.getMinDmg(), attacker.getMaxDmg())-defender.getActualDefense();
		defender.setCurrentHealth(defender.getCurrentHealth()-totalDamage);
		Point d=find(defender);
		System.out.println("took "+totalDamage+" damage");
		damageTaken[d.x][d.y] += totalDamage;
	}
	
	/**
	 * returns 1 if id is 0. Returns 0 if id is 1.
	 * @param id
	 * @return
	 */
	public static int otherPlayerId(int id){
		if(id==0)
			return 1;
		else
			return 0;
	}
	
	public Player getPlayer(int id){
		return player[id];
	}
	
	/**
	 * returns -1 if player is not found.
	 * @param p
	 * @return
	 */
	public int getBattleIdOfPlayer(Player p){
		if(player[0] == p)
			return 0;
		else if(player[1] == p)
			return 1;
		else
			return -1;
	}
	
	public void setReadyToFight(int playerNumber){
		isReadyToFight[playerNumber] = true;
	}
	
	/** tells if given player is ready **/
	public boolean getIsReadyToFight(int playerNumber){
		return isReadyToFight[playerNumber];
	}
	
	/**
	 * Returns true if it is safe to call fightBattle(). Otherwise false.
	 * Checks if all Creatures have selected targets.
	 * @return
	 */
	public boolean isReadyToStartBattle(){
		for(int i = 0; i<=1; i++){
			if(!isReadyToStart(i))
				return false;
			if(!isReadyToFight[i])
				return false;
		}
		return true;
	}
	
	public boolean isReadyToStart(int i){

		// removed to let creatures avoid attacking.
		/*for(CreatureCard c : creatures[i]){
			if(attackOrder[i][creatures[i].indexOf(c)] == -1)
				return false;
		}*/
		
		return true;
	}
	public int[][] getAttackOrder(){
		return attackOrder;
	}
	public int[][] getDamageTaken(){
		return damageTaken;
	}
	
}
